package com.example.common.util;

import cn.hutool.core.collection.CollUtil;
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;

/**
 * 转换工具类
 *
 * @author zjarlin
 * @see Class
 * @since 2022/06/15
 */
public interface Conversions {
    /**
     * LocalDate转Date
     *
     * @param localDate 当地日期
     * @return 返回时间
     * @author zjarlin
     * @since 2022/06/29
     */
    public static Date toDate(LocalDate localDate) {
        if (Objects.isNull(localDate)) {
            return null;
        }
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * LocalDateTime转Date
     *
     * @param localDateTime 当地日期时间
     * @return 返回时间
     * @author zjarlin
     * @since 2022/06/29
     */
    public static Date toDate(LocalDateTime localDateTime) {
        if (Objects.isNull(localDateTime)) {
            return null;
        }
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * Date转LocalDate
     *
     * @param date 日期
     * @return 返回日期
     * @author zjarlin
     * @since 2022/06/29
     */
    public static LocalDate toLocalDate(Date date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * Date转LocalDateTime
     *
     * @param date 日期
     * @return 返回时间
     * @author zjarlin
     * @since 2022/06/29
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    static String getWeek(final LocalDate localDate) {
        DayOfWeek dayOfWeek = localDate.getDayOfWeek();
        return getString(dayOfWeek);
    }

    static String getString(DayOfWeek dayOfWeek) {
        HashMap<DayOfWeek, String> hashMap = new HashMap<DayOfWeek, String>() {{
            put(DayOfWeek.MONDAY, "周一");
            put(DayOfWeek.TUESDAY, "周二");
            put(DayOfWeek.WEDNESDAY, "周三");
            put(DayOfWeek.THURSDAY, "周四");
            put(DayOfWeek.FRIDAY, "周五");
            put(DayOfWeek.SATURDAY, "周六");
            put(DayOfWeek.SUNDAY, "周日");
        }};
        String s = hashMap.get(dayOfWeek);
        return s;
    }

    static String getWeek(final LocalDateTime localDateTime) {
        DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
        return getString(dayOfWeek);
    }

    public static void main(String[] args) {
        Collection<LocalDate> localDates = getsACollectionOfAllDaysInASpecifiedMonth(2022, 9);
        System.out.println(localDates);
    }

    /**
     * 获取指定年月当月所有天数集合
     *
     * @param year
     * @param month 月 入参
     * @return {@link Collection }<{@link LocalDate }>
     * @author addzero
     * @since 2022/10/27
     */
    static Collection<LocalDate> getsACollectionOfAllDaysInASpecifiedMonth(final int year, final int month) {
        LocalDate of = LocalDate.of(year, month, 1);
        LocalDate firstDay = of.with(TemporalAdjusters.firstDayOfMonth()); // 获取当前月的第一天
        LocalDate lastDay = of.with(TemporalAdjusters.lastDayOfMonth()); // 获取当前月的最后一天
        long intervalNumber = firstDay.until(lastDay, ChronoUnit.DAYS) + 1;
        //long daysToSubtract = firstDay.toEpochDay();
        //日期差几天
        //LocalDate localDate = lastDay.minusDays(daysToSubtract);
        //时间间隔个数
        //long intervalNumber = localDate.toEpochDay()+1;
        Collection<LocalDate> collect1 = Stream.iterate(firstDay, e -> e.plusDays(1)).limit(intervalNumber).collect(Collectors.toSet());
        return collect1;
    }

    /**
     * 获取指定日期集合在当月中补集
     *
     * @param srcLocalDate 收集 入参
     * @return {@link Collection }<{@link LocalDate }>
     * @author addzero
     * @since 2022/10/27
     */
    static Collection<LocalDate> midMonthSupplement(Collection<LocalDate> srcLocalDate) {
        Integer year = srcLocalDate.stream().map(LocalDate::getYear).findAny().orElse(null);
        Integer month = srcLocalDate.stream().map(LocalDate::getMonthValue).findAny().orElse(null);
        //获取当月数据天数集合
        Collection<LocalDate> localDates = getsACollectionOfAllDaysInASpecifiedMonth(year, month);
        Collection<LocalDate> subtract = CollUtil.subtract(localDates, srcLocalDate);
        return subtract;
    }

    /**
     * 获取指定年月有多少个工作日）
     *
     * @param year
     * @param month
     * @return
     */
    public static int countWorkDay(int year, int month) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        // 月份是从0开始计算，所以需要减1
        c.set(Calendar.MONTH, month - 1);

        // 当月最后一天的日期
        int max = c.getActualMaximum(Calendar.DAY_OF_MONTH);
        // 开始日期为1号
        int start = 1;
        // 计数
        int count = 0;
        while (start <= max) {
            c.set(Calendar.DAY_OF_MONTH, start);
            if (isWorkDay(c)) {
                count++;
            }
            start++;
        }
        return count;
    }

    // 判断是否工作日（未排除法定节假日，由于涉及到农历节日，处理很麻烦）
    public static boolean isWorkDay(Calendar c) {
        // 获取星期,1~7,其中1代表星期日，2代表星期一 ... 7代表星期六
        int week = c.get(Calendar.DAY_OF_WEEK);
        // 不是周六和周日的都认为是工作日
        return week != Calendar.SUNDAY && week != Calendar.SATURDAY;
    }

    /**
     * 得到一天中最小时间和最大时间
     * <p>
     * 入参
     *
     * @return {@link Pair }<{@link LocalDateTime }, {@link LocalDateTime }>
     * @author addzero
     * @since 2022/10/31
     */
    static Pair<LocalDateTime, LocalDateTime> getMinMaxOfOneDay() {
        LocalDate now = LocalDate.now();
        //LocalDate of1 = LocalDate.of(2022, 10, 28);
        //LocalDateTime start = LocalDateTime.of(of1, LocalTime.MIN);
        //LocalDateTime end = LocalDateTime.of(of1, LocalTime.MAX);
        LocalDateTime start = LocalDateTime.of(now, LocalTime.MIN);
        LocalDateTime end = LocalDateTime.of(now, LocalTime.MAX);
        Pair<LocalDateTime, LocalDateTime> of = Pair.of(start, end);
        return of;
    }

    static Date addDays(Date date, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, days);
        return calendar.getTime();
    }
}
